/**
 * Copyright 2019 吉鼎科技.

 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package cn.easyplatform.web.task.zkex.simple;

import cn.easyplatform.EasyPlatformWithLabelKeyException;
import cn.easyplatform.lang.Nums;
import cn.easyplatform.lang.Strings;
import cn.easyplatform.messages.request.GetListRequestMessage;
import cn.easyplatform.messages.vos.GetListVo;
import cn.easyplatform.spi.service.ComponentService;
import cn.easyplatform.type.DeviceType;
import cn.easyplatform.type.FieldVo;
import cn.easyplatform.type.IResponseMessage;
import cn.easyplatform.web.contexts.Contexts;
import cn.easyplatform.web.ext.Assignable;
import cn.easyplatform.web.ext.ComponentBuilder;
import cn.easyplatform.web.ext.Node;
import cn.easyplatform.web.ext.Widget;
import cn.easyplatform.web.ext.zul.Surveybox;
import cn.easyplatform.web.service.ServiceLocator;
import cn.easyplatform.web.task.OperableHandler;
import cn.easyplatform.web.task.BackendException;
import org.apache.commons.lang3.ArrayUtils;
import org.mozilla.javascript.NativeArray;
import org.zkoss.util.resource.Labels;
import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.HtmlBasedComponent;
import org.zkoss.zul.*;

import java.util.*;

/**
 * @author <a href="mailto:davidchen@epclouds.com">littleDog</a> <br/>
 * @since 2.0.0 <br/>
 */
public class SurveyboxBuilder implements ComponentBuilder, Widget, Assignable {

    private OperableHandler main;

    private Surveybox sb;

    public SurveyboxBuilder(OperableHandler mainTaskHandler, Surveybox sb) {
        this.main = mainTaskHandler;
        this.sb = sb;
    }

    @Override
    public Component build() {
        if (Strings.isBlank(sb.getQuery()))
            throw new EasyPlatformWithLabelKeyException("component.property.not.found", "<surveybox>", "query");
        if (Strings.isBlank(sb.getGroupQuery()))
            throw new EasyPlatformWithLabelKeyException("component.property.not.found", "<surveybox>", "groupQuery");
        if (Strings.isBlank(sb.getDetailQuery()))
            throw new EasyPlatformWithLabelKeyException("component.property.not.found", "<surveybox>", "detailQuery");
        sb.setAttribute("$proxy", this);
        if (!sb.isDisabled() && main.getAccess() != null && !Strings.isBlank(sb.getAccess())) {
            String[] qx = sb.getAccess().split(",");
            boolean search = false;
            for (String power : qx) {
                if (ArrayUtils.contains(main.getAccess(), power)) {
                    search = true;
                    break;
                }
            }
            sb.setDisabled(!search);
        }
        if (Strings.isBlank(sb.getStyle()))
            sb.setStyle("overflow:auto");
        else
            sb.setStyle("overflow:auto;" + sb.getStyle());
        createComponent(sb);
        return sb;
    }

    private void createComponent(Surveybox sb) {
        sb.getChildren().clear();
        GetListVo gv = new GetListVo(sb.getDbId(), sb.getGroupQuery());
        gv.setFilter(sb.getFilter());
        GetListRequestMessage req = new GetListRequestMessage(main.getId(),
                gv);
        ComponentService cs = ServiceLocator
                .lookup(ComponentService.class);
        IResponseMessage<?> resp = cs.getList(req);
        if (resp.isSuccess()) {
            List<FieldVo[]> group = (List<FieldVo[]>) resp.getBody();
            if (!group.isEmpty()) {
                gv = new GetListVo(sb.getDbId(), sb.getQuery());
                req = new GetListRequestMessage(main.getId(),
                        gv);
                resp = cs.getList(req);
                if (!resp.isSuccess())
                    throw new BackendException(resp);
                List<FieldVo[]> data = (List<FieldVo[]>) resp.getBody();//索引0表示类型，索引1表示id，索引2表示题目,其余用户自定义
                Node root = createModel(cs, group, data);
                createRows(root.getChildren());
            }
        } else
            throw new BackendException(resp);
    }

    private void createRows(List<Node> children) {
        StringBuilder builder = new StringBuilder();
        for (int index = 0; index < children.size(); index++) {
            Node node = children.get(index);
            Div div = new Div();
            div.setWidth("100%");
            div.setHeight("100%");
            for (int i = 0; i < node.getLevel(); i++) {
                Span span = new Span();
                span.setSclass("z-tree-line");
                div.appendChild(span);
            }
            if (node.getData() == null) {//分类
                Label title = new Label();
                title.setSclass("h" + (node.getLevel() + 5));
                title.setStyle("font-weight:900");
                div.appendChild(title);
                sb.appendChild(div);
                if (node.getChildren() != null) {
                    builder.append(node.getIndex()).append(".").append(node.getName());
                    if (!node.getChildren().isEmpty() && node.getChildren().get(0).getData() != null) {
                        if (node.getChildren().get(0).getData().length > 4) {//多于4个栏位，第4位表示分数,第5答案
                            double scores = 0d;
                            for (Node n : node.getChildren()) {
                                if (n.getData()[3].getValue() != null)
                                    scores += Nums.toDouble(n.getData()[3].getValue(), 0d);
                            }
                            builder.append("(").append(Labels.getLabel("survey.title.count.2", new Object[]{node.getChildren().size(), scores})).append(")");
                        } else
                            builder.append("(").append(Labels.getLabel("survey.title.count.1", new Object[]{node.getChildren().size()})).append(")");
                    }
                    createRows(node.getChildren());
                }
                title.setValue(builder.toString());
            } else if (node.getName() != null) {//题目
                builder.append((index + 1)).append(".").append(node.getName());
                Label title = new Label(builder.toString());
                title.setSclass("h7");
                title.setStyle("font-weight:bold");
                div.appendChild(title);
                sb.appendChild(div);
                div.setAttribute("value", node);
                int type = Nums.toInt(node.getData()[0].getValue(), 1);
                if (sb.isShowAnswer() && (type == 1 || type == 2)) {
                    Label label = new Label(Labels.getLabel("survey.answer") + node.getData()[4].getValue());
                    label.setStyle("color:red");
                    Span span = new Span();
                    span.setSclass("z-tree-line");
                    span.setParent(div);
                    div.appendChild(label);
                }
                if (sb.getScoreType() > 0) {//答案
                    Span span = new Span();
                    span.setZclass("pull-right");
                    span.setStyle("margin-right:10px");
                    Label lbl = new Label(Labels.getLabel("survey.score"));
                    lbl.setStyle("color:red");
                    lbl.setParent(span);
                    if (type == 1 || (type == 2 && sb.isStrictMode())) {
                        Label tb = new Label();
                        tb.setStyle("font-weight:bold");
                        tb.setParent(span);
                    } else {
                        Doublebox tb = new Doublebox();
                        tb.setWidth("20px");
                        tb.setSclass("pull-right");
                        tb.setStyle("padding:0px;min-height:20px;font-weight:bold;color:red");
                        if (sb.getScoreType() != 2) {
                            if (node.getParent() instanceof NodeExt && "2".equals(((NodeExt) node.getParent()).getType()))
                                tb.setReadonly(true);
                        } else
                            tb.setReadonly(true);
                        tb.setValue(0);
                        tb.setParent(span);
                    }
                    span.setParent(div);
                }
                createDetail(node);
            }
            builder.setLength(0);
        }
        builder = null;
    }

    private void createDetail(Node node) {
        if (!node.isLeaf()) {
            int type = Nums.toInt(node.getData()[0].getValue(), 1);
            if (Contexts.getEnv().getDeviceType().equalsIgnoreCase(DeviceType.AJAX.getName())) {
                boolean isVlayout = node.getData().length > 5 ? "2".equals(node.getData()[5].getValue()) ? true : false : false;
                HtmlBasedComponent layout = null;
                if (isVlayout)
                    layout = new Vlayout();
                else {
                    if (type == 3 && sb.isShowAnswer()) {
                        layout = new Vlayout();
                    } else {
                        layout = new Div();
                        layout.setWidth("100%");
                        layout.setHeight("100%");
                        for (int i = 0; i < node.getLevel() + 1; i++) {
                            Span span = new Span();
                            span.setSclass("z-tree-line");
                            layout.appendChild(span);
                        }
                    }
                }
                if (type == 1) {//单选题
                    Radiogroup rg = new Radiogroup();
                    if (isVlayout) {
                        rg.appendChild(layout);
                        rg.setParent(sb);
                    } else {
                        layout.appendChild(rg);
                        layout.setParent(sb);
                    }
                    for (Node child : node.getChildren()) {
                        Component c = null;
                        if (isVlayout) {
                            c = new Div();
                            for (int i = 0; i < node.getLevel() + 1; i++) {
                                Span span = new Span();
                                span.setSclass("z-tree-line");
                                c.appendChild(span);
                            }
                        } else
                            c = rg;
                        Radio radio = new Radio(child.getId() + "、" + child.getName());
                        radio.setDisabled(sb.isDisabled());
                        radio.setValue(child);
                        if (isVlayout) {
                            c.appendChild(radio);
                            layout.appendChild(c);
                        } else {
                            c.appendChild(radio);
                            radio.setStyle("margin-right:" + sb.getItemSpacing());
                        }
                    }
                    return;
                } else if (type == 2) {//多选
                    for (Node child : node.getChildren()) {
                        Component c = null;
                        if (isVlayout) {
                            c = new Div();
                            for (int i = 0; i < node.getLevel() + 1; i++) {
                                Span span = new Span();
                                span.setSclass("z-tree-line");
                                c.appendChild(span);
                            }
                            layout.appendChild(c);
                        } else
                            c = layout;
                        Checkbox checkbox = new Checkbox(child.getId() + "、" + child.getName());
                        if (!isVlayout)
                            checkbox.setStyle("margin-right:" + sb.getItemSpacing());
                        checkbox.setValue(child);
                        checkbox.setDisabled(sb.isDisabled());
                        c.appendChild(checkbox);
                    }
                } else if (type == 3) {
                    Component c = null;
                    if (sb.isShowAnswer()) {
                        c = new Div();
                        for (int i = 0; i < node.getLevel() + 1; i++) {
                            Span span = new Span();
                            span.setSclass("z-tree-line");
                            c.appendChild(span);
                        }
                        layout.appendChild(c);
                    } else
                        c = layout;
                    Textbox textbox = new Textbox();
                    textbox.setHflex("1");
                    textbox.setReadonly(sb.isDisabled());
                    textbox.setParent(c);
                    int lines = Nums.toInt(node.getChildren().get(0).getId(), 1);
                    textbox.setRows(lines);
                    if (sb.isShowAnswer()) {
                        c = new Div();
                        for (int i = 0; i < node.getLevel() + 1; i++) {
                            Span span = new Span();
                            span.setSclass("z-tree-line");
                            c.appendChild(span);
                        }
                        Label label = new Label(Labels.getLabel("survey.answer"));
                        label.setStyle("color:red");
                        c.appendChild(label);
                        layout.appendChild(c);
                        c = new Div();
                        for (int i = 0; i < node.getLevel() + 1; i++) {
                            Span span = new Span();
                            span.setSclass("z-tree-line");
                            c.appendChild(span);
                        }
                        layout.appendChild(c);
                        textbox = new Textbox();
                        textbox.setHflex("1");
                        textbox.setReadonly(true);
                        textbox.setParent(c);
                        textbox.setValue((String) node.getData()[4].getValue());
                        textbox.setRows(lines);
                    }
                }
                sb.appendChild(layout);
            } else {
                if (type == 1) {//单选题
                    Radiogroup rg = new Radiogroup();
                    Vlayout vlayout = new Vlayout();
                    for (Node child : node.getChildren()) {
                        Div div = new Div();
                        for (int i = 0; i < node.getLevel() + 1; i++) {
                            Span span = new Span();
                            span.setSclass("z-tree-line");
                            div.appendChild(span);
                        }
                        Radio radio = new Radio(child.getId() + ". " + child.getName());
                        radio.setDisabled(sb.isDisabled());
                        radio.setValue(child);
                        div.appendChild(radio);
                        vlayout.appendChild(div);
                    }
                    vlayout.setParent(rg);
                    rg.setParent(sb);
                } else if (type == 2) {
                    Vlayout vlayout = new Vlayout();
                    for (Node child : node.getChildren()) {
                        Div div = new Div();
                        for (int i = 0; i < node.getLevel() + 1; i++) {
                            Span span = new Span();
                            span.setSclass("z-tree-line");
                            div.appendChild(span);
                        }
                        Checkbox checkbox = new Checkbox(child.getId() + ". " + child.getName());
                        checkbox.setValue(child);
                        checkbox.setDisabled(sb.isDisabled());
                        div.appendChild(checkbox);
                        vlayout.appendChild(div);
                    }
                    vlayout.setParent(sb);
                } else if (type == 3) {
                    Div div = new Div();
                    div.setStyle("width:100%;height:100%;display:flex");
                    for (int i = 0; i < node.getLevel() + 1; i++) {
                        Span span = new Span();
                        span.setSclass("z-tree-line");
                        div.appendChild(span);
                    }
                    Textbox textbox = new Textbox();
                    textbox.setHflex("1");
                    textbox.setReadonly(sb.isDisabled());
                    textbox.setParent(div);
                    int lines = Nums.toInt(node.getChildren().get(0).getId(), 1);
                    textbox.setRows(lines);
                    sb.appendChild(div);
                }
            }
        }
    }

    private Node createModel(ComponentService cs, List<FieldVo[]> group, List<FieldVo[]> data) {
        Node root = new Node(1);
        if (group.get(0).length == 3) {//仅单层结构:索引0表示当前id,索引1表示类型,索引2表示名称
            for (FieldVo[] fvs : group) {
                Node node = new Node(fvs[0].getValue(), (String) fvs[2].getValue());
                root.appendChild(node);
                int type = Nums.toInt(fvs[1].getValue(), 0);
                if (type > 0)
                    createNode(cs, node, type, data);
            }
        } else if (group.get(0).length >= 4) {//多层结构:索引0表示当前id,索引1表示上层id,索引2表示类型,索引3表示名称
            Map<Object, Node> map = new LinkedHashMap<>();
            List<Node> pending = new ArrayList<>();
            for (FieldVo[] fvs : group) {
                Node node = null;
                if (fvs.length == 4)
                    node = new Node(fvs[0].getValue(), fvs[1].getValue(), (String) fvs[3].getValue());
                else
                    node = new NodeExt(fvs[0].getValue(), fvs[1].getValue(), (String) fvs[3].getValue(), (String) fvs[4].getValue());
                int type = Nums.toInt(fvs[2].getValue(), 0);
                if (type > 0)
                    createNode(cs, node, type, data);
                Node parent = map.get(node.getParentId());
                if (parent != null)
                    parent.appendChild(node);
                else
                    pending.add(node);
                map.put(node.getId(), node);
            }
            Iterator<Node> itr = pending.iterator();
            while (itr.hasNext()) {
                Node node = itr.next();
                Node parent = map.get(node.getParentId());
                if (parent != null) {
                    parent.appendChild(node);
                    itr.remove();
                } else
                    root.appendChild(node);
                node.resetLevel();
            }
        }
        return root;
    }

    private void createNode(ComponentService cs, Node parent, int type, List<FieldVo[]> data) {
        Iterator<FieldVo[]> itr = data.iterator();
        while (itr.hasNext()) {
            FieldVo[] fvs = itr.next();
            if (Nums.toInt(fvs[0].getValue(), 0) == type) {
                //索引0表示类型，索引1表示id，索引2表示题目,如果大于4，索引3表示分数，索引4表示答案，其余用户自定义
                Node node = new Node(fvs[1].getValue(), fvs, (String) fvs[2].getValue());
                parent.appendChild(node);
                GetListVo gv = new GetListVo(sb.getDbId(), sb.getDetailQuery());
                gv.setParameter(node.getId());
                GetListRequestMessage req = new GetListRequestMessage(main.getId(),
                        gv);
                IResponseMessage<?> resp = cs.getList(req);
                if (!resp.isSuccess())
                    throw new BackendException(resp);
                List<FieldVo[]> details = (List<FieldVo[]>) resp.getBody();
                //索引0表示选项名称,索引1表示选项内容
                for (FieldVo[] detail : details)//id作为选项名称
                    node.appendChild(new Node(detail[0].getValue(), (String) detail[1].getValue()));
                itr.remove();
            }
        }
    }

    @Override
    public void setValue(Object value) {
        Map<String, Object[]> data = null;
        if (value != null && !value.toString().trim().equals("")) {
            if (value instanceof List<?>) {
                List<?> list = (List<?>) value;
                Object[] objs = new Object[list.size()];
                list.toArray(objs);
                value = objs;
            }
            if (value instanceof Object[]) {
                data = new HashMap<>();
                Object[] objs = (Object[]) value;
                for (Object obj : objs) {
                    if (obj instanceof FieldVo[]) {
                        FieldVo[] fvs = (FieldVo[]) obj;
                        Object[] tmp = new Object[fvs.length];
                        for (int i = 0; i < tmp.length; i++)
                            tmp[i] = fvs[i].getValue();
                        data.put(tmp[0].toString(), tmp);
                    } else if (obj instanceof NativeArray) {
                        NativeArray fvs = (NativeArray) obj;
                        Object[] tmp = new Object[fvs.size()];
                        for (int i = 0; i < tmp.length; i++)
                            tmp[i] = fvs.get(i);
                        data.put(tmp[0].toString(), tmp);
                    } else {
                        Object[] fvs = (Object[]) obj;
                        data.put(fvs[0].toString(), fvs);
                    }
                }
            } else if (value instanceof Map) {
                data = (Map<String, Object[]>) value;
            }
        }
        if (data != null) {
            Iterator<Component> itr = sb.queryAll("div").iterator();
            while (itr.hasNext()) {
                Component topics = itr.next();
                Node node = (Node) topics.getAttribute("value");
                if (node != null) {
                    Component div = topics.getNextSibling();
                    if (div == null) continue;
                    FieldVo[] fvs = node.getData();
                    int type = Nums.toInt(fvs[0].getValue(), 1);
                    Object[] vals = data.get(fvs[1].getValue().toString());
                    if (vals != null) {
                        if (type == 3) {
                            Textbox tbx = (Textbox) div.query("textbox");
                            tbx.setValue(vals[1] == null ? "" : vals[1].toString());
                            if (sb.getScoreType() > 0 && vals.length == 3) {//有分数栏位
                                Doublebox db = (Doublebox) topics.query("doublebox");
                                db.setValue(Nums.toDouble(vals[2], 0d));
                            }
                        } else {
                            if (type == 1) {
                                Radiogroup rg = (Radiogroup) div.query("radiogroup");
                                for (Radio radio : rg.getItems()) {
                                    Node child = radio.getValue();
                                    radio.setChecked(Objects.equals(child.getId(), vals[1]));
                                }
                                if (sb.getScoreType() > 0 && vals.length == 3) {//有分数栏位
                                    Label db = (Label) topics.getLastChild().query("label").getNextSibling();
                                    db.setValue(Nums.toInt(vals[2], 0) + "");
                                }
                            } else if (type == 2) {
                                Iterator<Component> chkItr = div.queryAll("checkbox").iterator();
                                while (chkItr.hasNext()) {
                                    Checkbox cbx = (Checkbox) chkItr.next();
                                    Node child = cbx.getValue();
                                    cbx.setChecked(vals[1] == null ? false : vals[1].toString().indexOf(child.getId().toString()) >= 0);
                                }
                                if (sb.getScoreType() > 0 && vals.length == 3) {//有分数栏位
                                    if (sb.isStrictMode()) {
                                        Label db = (Label) topics.getLastChild().query("label").getNextSibling();
                                        db.setValue(Nums.toInt(vals[2], 0) + "");
                                    } else {
                                        Doublebox db = (Doublebox) topics.query("doublebox");
                                        db.setValue(Nums.toDouble(vals[2], 0d));
                                    }
                                }
                            }//else
                        }//else
                    }//if
                }//if
            }//while
        }
    }

    @Override
    public Object getValue() {
        Map<Object, Object> data = new LinkedHashMap<>();
        Iterator<Component> itr = sb.queryAll("div").iterator();
        while (itr.hasNext()) {
            Component div = itr.next();
            Node node = (Node) div.getAttribute("value");
            if (node != null) {
                div = div.getNextSibling();
                FieldVo[] fvs = node.getData();
                int type = Nums.toInt(fvs[0].getValue(), 1);
                if (type == 1) {
                    Radiogroup rg = (Radiogroup) div.query("radiogroup");
                    if (rg.getSelectedIndex() >= 0) {
                        Node child = rg.getSelectedItem().getValue();
                        data.put(fvs[1].getValue(), child.getId());
                    }
                } else if (type == 2) {
                    StringBuilder sb = new StringBuilder();
                    Iterator<Component> chkItr = div.queryAll("checkbox").iterator();
                    while (chkItr.hasNext()) {
                        Checkbox cbx = (Checkbox) chkItr.next();
                        if (cbx.isChecked()) {
                            Node child = cbx.getValue();
                            sb.append(child.getId()).append(",");
                        }
                    }
                    if (sb.length() > 0) {
                        sb.deleteCharAt(sb.length() - 1);
                        data.put(fvs[1].getValue(), sb.toString());
                    }
                } else if (type == 3) {
                    Textbox tbx = (Textbox) div.query("textbox");
                    if (!Strings.isBlank(tbx.getValue()))
                        data.put(fvs[1].getValue(), tbx.getValue());
                }
            }
        }
        return data;
    }

    @Override
    public void reload(Component rg) {
        createComponent((Surveybox) rg);
    }

    class NodeExt extends Node {
        private String type;

        public NodeExt(Object id, Object parentId, String name, String type) {
            super(id, parentId, name);
            this.type = type;
        }

        public String getType() {
            return type;
        }
    }
}
